package com.demo.stampedLock;

import java.util.concurrent.locks.StampedLock;

//StampedLock可以认为他是读写锁的一种改进版本，读写锁虽然分离了读和写的功能，使得读与读之间可以完全并发，但是读和写之间依然是冲突的，读锁会完全阻塞写锁，
//他使用的依然是悲观的锁策略，如果有大量的读线程，他也有可能引起写线程的饥饿。
//而StampedLock提供了一种乐观的读策略，这种乐观的锁非常类似于无锁的操作，使得乐观锁完全不会阻塞写线程。
public class Point {

	//表示点的坐标
	private double x, y;
	private final StampedLock sl = new StampedLock();

	//这是一个排它锁
	void move(double deltaX, double deltaY){
		long stamp = sl.writeLock();
		try{
			x += deltaX;
			y += deltaY;
		}finally{
			sl.unlockWrite(stamp);
		}
	}

	//只读方法，读取x，y的坐标，在读取时，首先使用tryOptimisticRead方法，这个方法表示尝试一次乐观读。它会返回一个类似于时间戳的邮戳整数stamp。
	//这个stamp就可以作为这一次锁获取的凭证。
	double distanceFromOrigin(){
		long stamp = sl.tryOptimisticRead();
		//读取x，y的值，但是我们不确定这个x，y是否一致（在读取x的时候，可能其他线程改写了y的值，使得currentX，currentY处于不一致状态），
		//因此我们必须使用validate方法，判断这个stamp是否在读过程中发生修改。如果stamp没有被修改过，可以直接进行数据处理。
		//如果不可用，则意味着在读取过程中，可能被其它线程改写了数据，因此，可能出现脏读。如果出现，我们可以像处理cas那样在一个死循环中一直使用乐观读，直到成功为止。
		//也可以升级锁的级别，例如，升级到悲观锁，使用readLock获得悲观的读锁，并进一步读取数据，如果当前对象正在被修改，则读锁的申请可能导致线程被挂起。
		double currentX = x, currentY = y;
		if(!sl.validate(stamp)){
			stamp = sl.readLock();
			try{
				currentX = x;
				currentY = y;
			}finally{
				sl.unlockRead(stamp);
			}
		}
		return Math.sqrt(currentX*currentX + currentY*currentY);
	}

}
